<?php
require_once(dirname(__FILE__) . '/MakeTag.php');

/**
 * @package CryoBB
 * @filename Base.php
 * @version 1.0.0
 * @author Scott Martin <sjm.dev1[at]gmail[dot]com>, Josh Ribakoff, Jonah Dahlquist
 * @date November 5th, 2010
 * @license GNU GPL V3
 
 <CryoBB>
    Copyright (C) <2010>  <Scott Martin>, Josh Ribakoff, Jonah Dahlquist

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
 */

class CryoBB_Base
{

	// Error message
	const ERROR_INVALID_STRATEGY = 'Sorry, that strategy is invalid.';
	
	const STRATEGY_STRICT = 0;
	
	const STRATEGY_OVERLAPPING = 1;
	
	//the tags and definitions supplied by dev
	private $tags = array();
	
	//the emoticons supplied by dev
	private $emoticons = array();
	
	//the regexp for matching tags
	private $matchRegexp;
	
	/**
	 * Converts input string to HTML
	 *
	 * According to parameters supplied, parses input string containing bb code 
	 * and emoticons into their HTML equivelants.
	 *
	 * @access public
	 * @param string $str - the input string to be parsed for bb code and emoticons
	 * @param boolean $parseBB - true to parse bb code, false not to
	 * @param boolean $parseEmoticons - true to parse emoticons, false not to
	 * @param isXHTML - true to use <br /> line breaks, false to use <br> (php >= 5.3.0)
	 * @access public
	 * @return string - input string converted to HTML according to parameters
	 */
	public function make($str, $parseBB=true, $strategy=self::STRATEGY_STRICT, $parseEmoticons=true, $isXHTML=true)
	{
		
		//don't parse any tags if we don't have to
		if ($parseBB && !empty($this->tags) && (strpos($str, '[') !== false) && (strpos($str, ']') !== false))
		{
			//make the tags that do need parsed
			$replaceMatches	= $this->matchTags($strategy, $str);
			$str = $this->makeTags($str, $replaceMatches);
		}

		//parse emoticons
		if ($parseEmoticons)
		{
			$str = $this->makeEmoticons($str);
		}
		
		//make line breaks
		$str = $this->makeLineBreaks($str, $isXHTML);
		
		//and return this thing of beauty we have created
		return $str;
	}
	
    /**
     * Matches bb code tags and their contents
     *
     * Creates a new Strategy object and dispatches the matching process to it.
     *
     * @param integer $strategy Strategy to match tags with
     * @param string $str The string to match for bb code tags
     * @access private
     * @return array Array of matches found
     */
	private function matchTags($strategy, $str)
	{
		$strategyObject = null;
		
		$lookup = array(
			self::STRATEGY_STRICT => 'CryoBB_Strategy_Strict',
			self::STRATEGY_OVERLAPPING => 'CryoBB_Strategy_Overlapping'
		);
		
		if (!isset($lookup[$strategy])) {
			throw new Exception(self::ERROR_INVALID_STRATEGY);
			return;
		}
		
		$strategyObject = new $lookup[$strategy]($this->tags);
		
		return $strategyObject->matchTags($str);
	}
	
	
	/**
	 * Adds a bb code tag or multiple bb code tags to this bbcode ojbect
	 *
	 * Adds tag definitions to $this->tags properties.  Multiple tag definitions 
	 * can be added at once.  Checks to make sure tags are instances of CryoBB_Tag
	 *
	 * @param mixed $tags Array of tags, or many arguments
	 * @access public
	 */
	public function addTags($tags = null)
	{
		if (!is_array($tags)) {
			$tags = func_get_args();
		}
		
		foreach ($tags AS $tag)
		{
			$this->addTag($tag);
		}
		return $this;
	}
	
	/**
	 * Alias of addTags
	 * 
	 * @param CryoBB_Tag $tag Tag to add
	 */
	public function addTag(CryoBB_Tag $tag)
	{
		$this->tags[strtolower($tag->tag)] = $tag;
		return $this;
	}
	
	/**
	 * Adds emoticon definitions to this object.
	 *
	 * @param object $emoticons
	 * @access public
	 */
	public function addEmoticons(CryoBB_Emoticons $emoticons)
	{
		$this->emoticons = $emoticons;
		return $this;
	}
	
	/**
	 * Converts emoticon text to HTML
	 *
	 * Converts emoticon text to HTML (presumably images) based on the definition
	 * of the emoticons.
	 *
	 * @param string $str The main string of text
	 * @access private
	 */
	private function makeEmoticons($str)
	{
		if (!empty($this->emoticons))
		{
			$func = $this->emoticons->isCaseSensitive ? 'str_replace' : 'str_ireplace';
		
			return $func(
				array_keys($this->emoticons->emoticons),
				array_values($this->emoticons->emoticons),
				$str
			);
		}
		return $str;
	}
	
	/**
	 * Replaces bb code tags with their HTML equivelants
	 *
	 * Does the necessary magic to turn bb code tags, parameters, and contents 
	 * into the desired output based on tag definitions.
	 *
	 * @param string $str The main string of text
	 * @param array $matches - an array of bb code matches found in the seached 
	 *  string
	 * @access private
	 */
	private function makeTags($str, $matches)
	{
		while (isset($matches[0]))
		{
			$match = $matches[0];
			
			$matches = array_slice($matches, 1);
			
			$tagMaker = new CryoBB_MakeTag($this->tags, $match);
			$replacement = $tagMaker->makeTag($str, $matches);
			
			$str = substr_replace(
				$str,
				$replacement,
				$match->offset,
				$match->length
			);
		}
		return $str;
	}
	
	/**
	 * Replaces new lines with line breaks
	 *
	 * Replaces new lines with line breaks.  If php version is >= 5.3.0, boolean 
	 * value can be passed to make() and then passed to this method to change 
	 * XHTML style line breaks (<br />) to regular line breaks (<br>)
	 *
	 * @param boolean $isXHTML
	 * @access private
	 * @return void
	 */
	private function makeLineBreaks($str, $isXHTML)
	{
		return (version_compare(PHP_VERSION, '5.3.0') >= 0)
			? nl2br($str, $isXHTML)
			: nl2br($str);
	}
}